Skip to content

Issue#3828 Refatorar Mesa Diretora#3829

Open
LeandroJatai wants to merge 26 commits into
3.1.xfrom
3828_refatorar_mesa_diretora
Open

Issue#3828 Refatorar Mesa Diretora#3829
LeandroJatai wants to merge 26 commits into
3.1.xfrom
3828_refatorar_mesa_diretora

Conversation

@LeandroJatai
Copy link
Copy Markdown
Member

@LeandroJatai LeandroJatai commented Apr 14, 2026

Refatora Mesa Diretora para utilizar o Crud de forma simplifica da criação e edição de mesas e composições

Descrição

  • Foi criado dois CRUDs para MesaDiretora e ComposicaoMesa, além dos forms correspondentes para criação e edição com regras de negócio ligadas aos dois modelos.
  • A MesaDiretora foi desligada de SessãoLegislativa e ligada à Legislatura. Os migrations cuidam dessa reassociação.
  • A tela de acesso público agora seleciona apenas legislatura, TABs são montados com quantas mesas tiver na legislatura selecionada. A navegação dos TABs é transformada em dropdown em telas menores
  • Após o merge do PR um commit deve ser feito após a execução de npm run build

Issue Relacionada

#3828
Nesta issue estão os requisitos funcionais e não funcionais, além da citação de várias outras issues que envolvem o tema

Motivação e Contexto

  • Mesas Diretoras nunca tiveram ligação conceitual com sessão legislativa. Sessão Legislativa possui uma definição e existência específica: inicia-se em meados de fevereiro e termina-se em meados de dezembro e diz sobre o intervalo onde haverá sessões plenárias.

  • Mesa diretora não: Mesa diretora, salvo raríssimas exceções, iniciam-se em 1º de janeiro e conclui-se com um ou dois anos. Dentro das exceções, estão a finalização inesperada de uma mesa e inicio de outra, seja por cassação de algum parlamentar, ou outro motivo qualquer que interrompa uma mesa e inicia-se outra.

  • Regras de não interseção entre mesas; de contenção em legislatura; de cargo único (que já existia); de só parlamentares da legislatura; de não duplicidade de parlamentar foram colocadas nos forms.

Como Isso Foi Testado?

foi criado o sapl/parlamentares/tests/test_mesadiretora.py que testa os forms

Capturas de Tela (se apropriado):

Tela Pública com 3 Mesas na mesma Legislatura. Nota-se os TABs com as três mesas, onde o Biênio de 23/24 possui duas mesas com datas de inicio e encerramento.
image

Tela de listagem do CRUDs tradicional para usuário que possui permissão de edição de mesa:
image
image

Tela de listagem das composições de uma mesa
image

Tipos de Mudanças

  • Bug fix (alteração que corrige uma issue e não altera funcionalidades já existentes)
  • Nova feature (alteração que adiciona uma funcionalidade e não altera funcionalidades já existentes)
  • Alteração disruptiva (Breaking change) (Correção ou funcionalidade que causa alteração nas funcionalidades existentes)

Checklist:

  • Eu li o documento de Contribuição (CONTRIBUTING).
  • Meu código segue o estilo de código deste projeto.
  • Minha alteração requer uma alteração na documentação. (Apostila das Oficinas)
  • Eu atualizei a documentação de acordo.
  • Eu adicionei testes para cobrir minhas mudanças.
  • Todos os testes novos e existentes passaram.

Copy link
Copy Markdown
Contributor

@edwardoliveira edwardoliveira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A motivação conceitual do PR é correta (Mesa Diretora pertence à Legislatura, não à
SessaoLegislativa). Isso é uma das muitas "heranças malditas" da modelagem de dados (mal feita!) do SAPL 2.5 e anteriores. A simplificação do AJAX/jQuery para CRUDs padrão é bem vinda. Os problemas abaixo precisam ser resolvidos antes do merge.

Comment thread sapl/parlamentares/migrations/0048_auto_20260413_1049.py Outdated
Comment thread sapl/parlamentares/migrations/0048_auto_20260413_1049.py Outdated
Comment thread sapl/parlamentares/migrations/0048_auto_20260413_1049.py Outdated
Comment thread sapl/parlamentares/forms.py Outdated
Comment thread sapl/parlamentares/models.py Outdated
Comment thread sapl/templates/parlamentares/mesadiretora_filter.html Outdated
Comment thread sapl/templates/crud/list.html Outdated
Comment thread sapl/parlamentares/views.py Outdated
Comment thread sapl/parlamentares/forms.py Outdated
Comment thread sapl/templates/parlamentares/mesadiretora_filter.html
@LeandroJatai LeandroJatai removed the request for review from joaohortsenado April 18, 2026 01:32

url(r'^mesa-diretora/remove-parlamentar-composicao/$',
remove_parlamentar_composicao, name='remove_parlamentar_composicao'),
url(r'^mesa-diretora/', include(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL renomeada quebra redireciona_urls

A URL sapl.parlamentares:mesa_diretora foi renomeada para mesadiretora_list, mas duas referências fora do diff continuam apontando para o nome antigo:

  • sapl/redireciona_urls/views.py:34parlamentar_mesa_diretora = (app_parlamentares + ':mesa_diretora'). Vai disparar UnknownUrlNameError quando alguém acessar /consultas/mesa_diretora/mesa_diretora_index_html (uso em views.py:495).
  • sapl/redireciona_urls/tests.py:140reverse('sapl.parlamentares:mesa_diretora') quebra a suíte.

Atualizar ambos para mesadiretora_list.

migrations.AlterField(
model_name='mesadiretora',
name='data_inicio',
field=models.DateField(verbose_name='Data Início'),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migration pode falhar em bases legadas

O modelo antigo permitia data_inicio/data_fim nulos em MesaDiretora (null=True). Fluxo atual:

  • 0046 popula essas datas a partir de sessao_legislativa, mas só replica os valores que a sessão tinha.
  • 0048 popula titulo apenas onde data_inicio IS NOT NULL AND data_fim IS NOT NULL.
  • 0049 (esta migration) aplica NOT NULL em ambas.

Se houver MesaDiretora cuja sessão não tinha datas (ou o join falhar), 0049 quebra com IntegrityError no upgrade. Como as bases dos clientes são bem heterogêneas, sugiro:

  1. Em 0046 (ou nova migration intermediária), preencher fallback antes do AlterField para NOT NULL — por exemplo, copiar das datas da legislatura quando a sessão não tiver.
  2. Rodar a migration contra alguns dumps reais antes do release.

def __init__(self, *args, **kwargs):
super(ComposicaoMesaForm, self).__init__(*args, **kwargs)
self.instance.mesa_diretora = self.initial.get('mesa_diretora')
self.fields['parlamentar'].queryset = self.fields['parlamentar'].queryset.filter(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ComposicaoMesaForm.__init__ quebra silenciosamente sem initial

Se o form for instanciado sem initial['mesa_diretora'], self.initial.get(...) retorna None e a próxima linha lança AttributeError: 'NoneType' object has no attribute 'legislatura'.

Hoje as views CRUD sempre passam initial, então não dispara em produção, mas qualquer caller futuro (admin, comando, outra view) quebra silenciosamente. Sugestão de guarda:

mesa = self.initial.get('mesa_diretora')
if mesa is not None:
    self.instance.mesa_diretora = mesa
    self.fields['parlamentar'].queryset = (
        self.fields['parlamentar'].queryset
        .filter(mandato__legislatura=mesa.legislatura)
    )

ultima_filiacao = self.filiacao_set.order_by('-data').first()
# este método conta com a ordenação default do model Filiacao para trazer a última filiação primeiro
# se order_by for adicionado aqui, o prefetch_related que inclui filiacao_set não irá pré-carregar como esperado
ultima_filiacao = self.filiacao_set.first()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filiacao_atual agora depende da ordenação default — vale um teste regressivo

A troca por self.filiacao_set.first() é correta porque Filiacao.Meta.ordering = ('parlamentar', '-data', '-data_desfiliacao'), e o prefetch_related adicionado na ListView depende disso para não disparar query extra. O comentário deixa o motivo claro — ótimo.

Sugestão: adicionar um teste curto que verifique o invariante (criar duas filiações em datas diferentes e checar parlamentar.filiacao_atual). Se um dia alguém mexer no Meta.ordering, o teste pega.


def clean(self):
if self.data_inicio and self.data_fim:
if self.data_inicio >= self.data_fim:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validação >= rejeita mesas de um único dia

Há cenários reais de mesa que dura um único dia (dissolução e recriação no mesmo dia, por cassação ou eventos extraordinários — exatamente o tipo de exceção citado na descrição do PR).

Considerar > em vez de >=, esse requisito precisa ser refinado... o que acha @edwardoliveira


def get_filterset_kwargs(self, filterset_class):
fk = super().get_filterset_kwargs(filterset_class)
if 'legislatura' not in self.request.GET and not 'mesa' in self.request.GET:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: not 'mesa' in self.request.GET'mesa' not in self.request.GET (PEP 8). Mesma observação na linha de baixo.

return _('Mesa da %(sessao)s sessao da %(legislatura)s Legislatura') % {
'sessao': self.sessao_legislativa, 'legislatura': self.sessao_legislativa.legislatura
}
return self.titulo or _('%(legislatura)s - %(data_inicio)s a %(data_fim)s') % {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datas em __str__ saem em ISO (2021-01-01) em vez do formato pt-br. Como titulo agora é o caminho principal, é raro cair no fallback, mas vale usar django.utils.formats.date_format(d, "SHORT_DATE_FORMAT") para consistência com o resto da aplicação.

{% endblock actions_search %}

{% block container_table_list %}
{% if perms.parlamentares.add_mesadiretora %}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

o template usa add_mesadiretora para alternar entre listagem CRUD (admin) e visualização em abas (público). Funcionalmente está consistente com o resto do SAPL. Só ponderando se change_mesadiretora não seria semanticamente mais correto para representar "tem painel de gestão".

{{composicao.parlamentar.nome_parlamentar}}</a>
</div>
</td>
<td>{{composicao.parlamentar.filiacao_atual}}</td>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partido pode divergir do partido da época

A view antiga (partido_parlamentar_sessao_legislativa) calculava a filiação na data da sessão. Esta agora mostra sempre a filiação mais recente. Para mesas atuais é a mesma coisa; para mesas históricas pode mostrar partido errado (ex.: parlamentar que mudou de partido depois).

É bom validar esse requisito também, @edwardoliveira . Se não for o caso, vale reintroduzir a lógica por data (ex.: método filiacao_em(data) no model).

'cargo': new_cargo.id,
})

composicao.refresh_from_db()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a cobertura dos forms está boa. mas acho que tem alguns pontos faltantes:

  1. MesaDiretoraFilterSet: testar que ?mesa=<id> resolve para a legislatura correta e que sem GET cai na legislatura atual; testar 404 para mesa inexistente.
  2. View pública (mesadiretora_filter.html): renderizar a página sem permissão de admin e validar as abas.
  3. test_composicaomesa_form_view_create/update: não fazem assert de status HTTP — adicionar pelo menos assert response.status_code in (200, 302) para detectar erros de form silenciosos.
  4. Variáveis mandato/mandato1/mandato2 criadas mas nunca lidas — substituir por _ = baker.make(...) para deixar a intenção clara.

NIT: faltou newline final no arquivo. (conferir isso nos demais arquivos)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants